/**
 * Created with hong.rong
 * Description:*
 * Date:2020/06/25
 */

import React, { Component } from 'react'
import autoHeight from '../autoHeight'
import styles from './index.less'

/* eslint no-return-assign: 0 */
/* eslint no-mixed-operators: 0 */
// riddle: https://riddle.alibaba-inc.com/riddles/2d9a4b90

class WaterWave extends Component {
  state = {
    radio: 1,
  }

  timer = 0

  root = null

  node = null

  componentDidMount() {
    this.renderChart()
    this.resize()
    window.addEventListener(
      'resize',
      () => {
        requestAnimationFrame(() => this.resize())
      },
      { passive: true }
    )
  }

  componentDidUpdate(props) {
    const { percent } = this.props
    if (props.percent !== percent) {
      // 不加这个会造成绘制缓慢
      this.renderChart('update')
    }
  }

  componentWillUnmount() {
    cancelAnimationFrame(this.timer)
    if (this.node) {
      this.node.innerHTML = ''
    }
    window.removeEventListener('resize', this.resize)
  }

  resize = () => {
    if (this.root) {
      const { height = 1 } = this.props
      const { offsetWidth } = this.root.parentNode
      this.setState({
        radio: offsetWidth < height ? offsetWidth / height : 1,
      })
    }
  }

  renderChart(type) {
    const { percent, color = '#1890FF' } = this.props
    const data = percent / 100
    const self = this
    cancelAnimationFrame(this.timer)

    if (!this.node || (data !== 0 && !data)) {
      return
    }

    const canvas = this.node
    const ctx = canvas.getContext('2d')
    if (!ctx) {
      return
    }
    const canvasWidth = canvas.width
    const canvasHeight = canvas.height
    const radius = canvasWidth / 2
    const lineWidth = 2
    const cR = radius - lineWidth

    ctx.beginPath()
    ctx.lineWidth = lineWidth * 2

    const axisLength = canvasWidth - lineWidth
    const unit = axisLength / 8
    const range = 0.2 // 振幅
    let currRange = range
    const xOffset = lineWidth
    let sp = 0 // 周期偏移量
    let currData = 0
    const waveupsp = 0.005 // 水波上涨速度

    let arcStack = []
    const bR = radius - lineWidth
    const circleOffset = -(Math.PI / 2)
    let circleLock = true

    for (
      let i = circleOffset;
      i < circleOffset + 2 * Math.PI;
      i += 1 / (8 * Math.PI)
    ) {
      arcStack.push([radius + bR * Math.cos(i), radius + bR * Math.sin(i)])
    }

    const cStartPoint = arcStack.shift()
    ctx.strokeStyle = color
    ctx.moveTo(cStartPoint[0], cStartPoint[1])

    function drawSin() {
      if (!ctx) {
        return
      }
      ctx.beginPath()
      ctx.save()

      const sinStack = []
      for (let i = xOffset; i <= xOffset + axisLength; i += 20 / axisLength) {
        const x = sp + (xOffset + i) / unit
        const y = Math.sin(x) * currRange
        const dx = i
        const dy = 2 * cR * (1 - currData) + (radius - cR) - unit * y

        ctx.lineTo(dx, dy)
        sinStack.push([dx, dy])
      }

      const startPoint = sinStack.shift()

      ctx.lineTo(xOffset + axisLength, canvasHeight)
      ctx.lineTo(xOffset, canvasHeight)
      ctx.lineTo(startPoint[0], startPoint[1])

      const gradient = ctx.createLinearGradient(0, 0, 0, canvasHeight)
      gradient.addColorStop(0, '#ffffff')
      gradient.addColorStop(1, color)
      ctx.fillStyle = gradient
      ctx.fill()
      ctx.restore()
    }

    function render() {
      if (!ctx) {
        return
      }
      ctx.clearRect(0, 0, canvasWidth, canvasHeight)
      if (circleLock && type !== 'update') {
        if (arcStack.length) {
          const temp = arcStack.shift()
          ctx.lineTo(temp[0], temp[1])
          ctx.stroke()
        } else {
          circleLock = false
          ctx.lineTo(cStartPoint[0], cStartPoint[1])
          ctx.stroke()
          arcStack = []

          ctx.globalCompositeOperation = 'destination-over'
          ctx.beginPath()
          ctx.lineWidth = lineWidth
          ctx.arc(radius, radius, bR, 0, 2 * Math.PI, true)

          ctx.beginPath()
          ctx.save()
          ctx.arc(radius, radius, radius - 3 * lineWidth, 0, 2 * Math.PI, true)

          ctx.restore()
          ctx.clip()
          ctx.fillStyle = color
        }
      } else {
        if (data >= 0.85) {
          if (currRange > range / 4) {
            const t = range * 0.01
            currRange -= t
          }
        } else if (data <= 0.1) {
          if (currRange < range * 1.5) {
            const t = range * 0.01
            currRange += t
          }
        } else {
          if (currRange <= range) {
            const t = range * 0.01
            currRange += t
          }
          if (currRange >= range) {
            const t = range * 0.01
            currRange -= t
          }
        }
        if (data - currData > 0) {
          currData += waveupsp
        }
        if (data - currData < 0) {
          currData -= waveupsp
        }
        sp += 0.07
        drawSin()
      }
      self.timer = requestAnimationFrame(render)
    }
    render()
  }

  render() {
    const { radio } = this.state
    const { percent, title, height = 1 } = this.props
    return (
      <div
        className={styles.waterWave}
        ref={n => (this.root = n)}
        style={{ transform: `scale(${radio})` }}
      >
        <div style={{ width: height, height, overflow: 'hidden' }}>
          <canvas
            className={styles.waterWaveCanvasWrapper}
            ref={n => (this.node = n)}
            width={height * 2}
            height={height * 2}
          />
        </div>
        <div className={styles.text} style={{ width: height }}>
          {title && <span>{title}</span>}
          <h4>{percent}%</h4>
        </div>
      </div>
    )
  }
}

export default autoHeight()(WaterWave)
